Skip to content

Add research digest: POST /research sends Gemini-powered email on any topic - #30

Closed
krMaynard wants to merge 5 commits into
mainfrom
claude/email-context-storage-38hu6
Closed

Add research digest: POST /research sends Gemini-powered email on any topic#30
krMaynard wants to merge 5 commits into
mainfrom
claude/email-context-storage-38hu6

Conversation

@krMaynard

@krMaynard krMaynard commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds POST /research endpoint that uses Gemini (the operator-configured GEMINI_MODEL) with Google Search grounding to research a configurable topic, then sends the summary directly to your Gmail inbox
  • New assistant/researcher.py module with research_topic() — single Gemini call with live web search, 2048 token cap, rejects empty/blocked responses
  • New send_message() in email_reader.py — uses existing gmail.modify scope (no re-auth needed), UTF-8 encoded
  • RESEARCH_TOPIC env var controls what to research (e.g. "AI and LLM news this week"); missing topic returns 400
  • Idempotent: the last sent (topic, date) is recorded in last_research.json in GCS, so a Cloud Scheduler retry after a timeout won't send the same digest twice on the same day (mirrors the /standup dedup pattern). Changing the topic sends a fresh digest
  • README updated with Cloud Scheduler setup, env var docs, and architecture diagram

Test plan

  • Set RESEARCH_TOPIC env var and hit POST /research locally — confirm email arrives in inbox
  • Hit POST /research twice in a row — confirm the second returns {"skipped": true, "reason": "already_sent_today"}
  • Deploy to Cloud Run and create a Cloud Scheduler job pointing at /research
  • Check Cloud Run logs for Research email sent to ...
# Set topic
gcloud run services update assistant \
  --region us-central1 \
  --update-env-vars RESEARCH_TOPIC="AI and LLM news this week"

# Test locally
curl -X POST http://localhost:8080/research

# Add Cloud Scheduler job
gcloud scheduler jobs create http research-digest \
  --schedule="0 7 * * *" \
  --uri="$SERVICE_URL/research" \
  --http-method=POST \
  --oidc-service-account-email=cloud-scheduler-invoker@YOUR_PROJECT.iam.gserviceaccount.com \
  --location=us-central1 \
  --time-zone="America/Los_Angeles"

🤖 Generated with Claude Code

https://claude.ai/code/session_01CSnZNqUEh8uDyMCPDmzBTa

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new research digest feature that uses Gemini with Google Search grounding to research a configured topic and email the summary directly to the user. Key changes include a new /research endpoint, a researcher module, and a helper to send emails immediately via the Gmail API. The review feedback highlights three important improvements: handling missing environment variables gracefully with a 400 Bad Request, adding defensive checks for empty or blocked Gemini responses, and explicitly specifying UTF-8 encoding when creating MIME text to prevent encoding errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread main.py Outdated
Comment thread assistant/researcher.py
Comment thread assistant/email_reader.py Outdated
@krMaynard krMaynard added the codex-review Queued for automated Codex review label Jul 18, 2026
@krMaynard

Copy link
Copy Markdown
Owner Author

Codex review

Fixed one concrete configuration regression in ba8d973: research digests now use GEMINI_MODEL (the same operator-controlled model as the rest of the service) instead of silently hardcoding gemini-2.0-flash. Added tests proving the configured model is passed through and empty responses remain rejected.

Validation: deterministic suite — 2 passed; git diff --check passed; no unresolved review threads.

One product-level blocker remains before merge: POST /research sends immediately but records no completed run. A Cloud Scheduler retry after a timeout can therefore send the same dated digest twice. The PR is also conflicting with main and should reconcile with the newer StateStore; that store is the natural place for a per-topic/per-date idempotency key. I did not guess those persistence semantics in this review.

@krMaynard krMaynard added codex-reviewed Latest revision reviewed by Codex and removed codex-review Queued for automated Codex review labels Jul 18, 2026
claude and others added 4 commits July 18, 2026 00:20
…ny topic

Uses Gemini 2.0 Flash with Google Search grounding to research a configurable
topic and sends the summary directly to the authenticated Gmail account.
Triggered by Cloud Scheduler on whatever cadence the user configures.

New: assistant/researcher.py, send_message() in email_reader.py, /research
route in main.py. README updated with setup instructions and env var docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSnZNqUEh8uDyMCPDmzBTa
- Return 400 Bad Request when RESEARCH_TOPIC env var is not set
- Raise ValueError on empty/blocked Gemini response in research_topic()
- Use MIMEText(..., "plain", "utf-8") in send_message() to handle non-ASCII

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSnZNqUEh8uDyMCPDmzBTa
…etry

POST /research sent immediately but recorded no completed run, so a Cloud
Scheduler retry after a timeout could send the same dated digest twice.

Mirror the /standup dedup pattern: persist the last sent (topic, date) to
last_research.json in GCS and skip if today's digest for the current topic
already went out. A changed RESEARCH_TOPIC still sends a fresh digest.

Also use the get_user_email() helper (with its HttpError handling) instead
of a raw getProfile call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSnZNqUEh8uDyMCPDmzBTa
@krMaynard
krMaynard force-pushed the claude/email-context-storage-38hu6 branch from ba8d973 to 4b93cf5 Compare July 18, 2026 00:24
@github-actions github-actions Bot added codex-review Queued for automated Codex review and removed codex-reviewed Latest revision reviewed by Codex labels Jul 18, 2026

Copy link
Copy Markdown
Owner Author

Addressed the two remaining items from the Codex review:

  • Merge conflict with main — rebased onto main (312e9d8). Resolved conflicts in main.py (both /availability-sync and /research now coexist) and README.md. PR is mergeable again.
  • Double-send idempotencyPOST /research now records the last sent (topic, date) in last_research.json in GCS and skips if today's digest for the current topic already went out, so a Cloud Scheduler retry after a timeout won't send twice. A changed RESEARCH_TOPIC still sends a fresh digest. This mirrors the existing /standup dedup pattern (there is no StateStore class on main — the pattern is the load_last_*/save_last_* GCS helpers, which is what I followed).

Full suite: 21 passed, 8 skipped (Gemini eval tests skip without a key locally).


Generated by Claude Code

@krMaynard krMaynard added codex-reviewed Latest revision reviewed by Codex and removed codex-review Queued for automated Codex review labels Jul 18, 2026
@krMaynard

Copy link
Copy Markdown
Owner Author

Codex re-review at 2657ba0: the new read-then-send idempotency check could still duplicate a digest when overlapping Cloud Scheduler retries both read GCS before either request saved the record. I replaced it with an atomic per-topic/date GCS reservation using if_generation_match=0; only one request can create the marker. Definite pre-delivery failures release it for retry, while an ambiguous Gmail send failure retains it to favor duplicate prevention. Added focused atomicity/topic-scope/release tests and updated the documentation.

Validation: deterministic suite 23 passed; compileall and git diff --check passed.

@github-actions github-actions Bot added codex-review Queued for automated Codex review and removed codex-reviewed Latest revision reviewed by Codex labels Jul 18, 2026
@krMaynard krMaynard added codex-reviewed Latest revision reviewed by Codex and removed codex-review Queued for automated Codex review labels Jul 18, 2026
@krMaynard krMaynard closed this Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-reviewed Latest revision reviewed by Codex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants